#ifndef _AS_FSM_H_
#define _AS_FSM_H_

#include "ASMessage.h"

NS_AS_BEGIN

	template <class entity_type>
class ASState
{
public:

	virtual ~ASState(){}

	//this will execute when the state is entered
	virtual void enter(entity_type* pEntity)=0;

	//this is the states normal update function
	virtual void execute(entity_type* pEntity,float dt)=0;

	//this will execute when the state is exited. 
	virtual void exit(entity_type* pEntity)=0;

	//this executes if the agent receives a message from the 
	//message dispatcher
	virtual void onHandleMessage(entity_type* pEntity,const ASMessage& msg)=0;
};

template <class entity_type>
class ASStateMachine
{
private:

	//a pointer to the agent that owns this instance
	entity_type*          _pOwner;

	ASState<entity_type>*   _pCurrentState;

	//a record of the last state the agent was in
	ASState<entity_type>*   _pPreviousState;

	//this is called every time the FSM is updated
	ASState<entity_type>*   _pGlobalState;


public:

	ASStateMachine(entity_type* owner):
		_pOwner(owner),
		_pCurrentState(nullptr),
		_pPreviousState(nullptr),
		_pGlobalState(nullptr)
	{}

	virtual ~ASStateMachine(){}

	//use these methods to initialize the FSM
	void setCurrentState(ASState<entity_type>* s){_pCurrentState = s;}
	void setGlobalState(ASState<entity_type>* s) {_pGlobalState = s;}
	void setPreviousState(ASState<entity_type>* s){_pPreviousState = s;}

	//call this to update the FSM
	void  update(float dt)const
	{
		//if a global state exists, call its execute method, else do nothing
		if(_pGlobalState)   _pGlobalState->execute(_pOwner,dt);

		//same for the current state
		if (_pCurrentState) _pCurrentState->execute(_pOwner,dt);
	}

	void  onHandleMessage(const ASMessage& msg)const
	{
		//first see if the current state is valid and that it can handle
		//the message
		if (_pCurrentState )
		{
			_pCurrentState->onHandleMessage(_pOwner, msg);
		}

		//if not, and if a global state has been implemented, send 
		//the message to the global state
		if (_pGlobalState )
		{
			_pGlobalState->onHandleMessage(_pOwner, msg);
		}
	}

	//change to a new state
	void  changeState(ASState<entity_type>* pNewState)
	{
		ASAssert(pNewState,"<ASStateMachine::changeState>:trying to assign null state to current");
		//assert(pNewState && );

		//keep a record of the previous state
		_pPreviousState = _pCurrentState;

		//call the exit method of the existing state
		_pCurrentState->exit(_pOwner);

		//change state to the new state
		_pCurrentState = pNewState;

		//call the entry method of the new state
		_pCurrentState->enter(_pOwner);
	}

	//change state back to the previous state
	void  revertToPreviousState()
	{
		changeState(_pPreviousState);
	}

	//returns true if the current state's type is equal to the type of the
	//class passed as a parameter. 
	bool  isInState(const ASState<entity_type>& st)const
	{
		if (typeid(*_pCurrentState) == typeid(st)) return true;
		return false;
	}

	ASState<entity_type>*  getCurrentState()  const{return _pCurrentState;}
	ASState<entity_type>*  getGlobalState()   const{return _pGlobalState;}
	ASState<entity_type>*  getPreviousState() const{return _pPreviousState;}

	//only ever used during debugging to grab the name of the current state
	std::string         getNameOfCurrentState()const
	{
		std::string s(typeid(*_pCurrentState).name());

		////remove the 'class ' part from the front of the string
		//if (s.size() > 5)
		//{
		//	s.erase(0, 6);
		//}

		return s;
	}
};



NS_AS_END
#endif